[TV] Search: Podcasts/Episodes scope filter - #5729
Conversation
af82e68 to
3414c70
Compare
|
@claude please review this |
|
Claude finished @sztomek's task in 2m 33s —— View job Code review: TV Search Podcasts/Episodes scope filter
Nice, focused change — the scope switching reads cleanly and splitting 1. Focus is lost in the Episodes scope after the podcast overlay closes
Related, milder: in 2. No explicit focus requester on the selected pill
Since selection follows focus here, any case where the restorer's saved child is dropped means focus lands on Top Results and the whole content region silently swaps out from under the user. Worth exercising: select Episodes → D-pad down into the list → scroll a few items → D-pad up back to the pills. If focus doesn't return to Episodes, the scope resets. 3.
|
7bc70d4 to
17d971b
Compare
d134ceb to
b88363a
Compare
|
Claude finished @sztomek's task in 3m 51s —— View job Code review: TV Search Podcasts/Episodes scope filter
Re-reviewed after the three follow-up commits. The two things I called out last time as the real risks are both fixed: I've left five inline comments. Summary below, most-significant first. 1. Episodes-scope grid drops the episode-actions affordance (inline)The new two-column grid reaches The result is that narrowing to the scope dedicated to episodes is what makes the episode actions invisible. A held D-pad centre isn't something a TV user discovers, and there's no on-screen hint. This is the one item I'd want resolved before merge; everything else below is polish. Secondary effect worth checking on device: once 2. The three scopes don't share a left edge (inline)
So Top Results → Podcasts shifts the covers 16dp left of the pills directly above them, and Podcasts → Episodes shifts them back. A Same comment covers 3. The pill bar is now duplicated verbatim, and it already cost something (inline)
4. The tab-bar colour change is global, not search-local (inline)
5. No tests for the new state (inline covers the adjacent nit)
6. Smaller notes
Verdict: #1 is the one I'd fix before merge — it's a real affordance regression inside the feature this PR adds, and it contradicts the same screen one pill over. #2 is a visible 16dp jump that a designer will file anyway. The rest can land as follow-ups. Reviewed the diff statically — I did not build the module or run it on a device, so the focus-behaviour notes in #6 are reasoning from the code, not observed. |
b88363a to
4cdc38e
Compare
|
Claude finished @sztomek's task in 3m 59s —— View job Code review: TV Search Podcasts/Episodes scope filter
Re-reviewed after the amend to 1. The new focus-entry mechanism (inline)The intent is right — the old
2. Still open from the previous passUnchanged in this push, so the earlier inline threads still apply:
3. Tests
4. Smaller notes
Verdict: the Reviewed the diff statically — I did not build the module or run it on a device, so the focus-behaviour notes are reasoning from the modifier chains, not observed. |
| LaunchedEffect(Unit) { | ||
| runCatching { searchFieldFocusRequester.requestFocus() } | ||
| } | ||
| Column(modifier = modifier.fillMaxSize()) { |
There was a problem hiding this comment.
This is the one substantive change since the last push, and it's worth calling out because it swaps a declarative focus contract for an imperative one-shot.
Before:
Column(
modifier = modifier
.fillMaxSize()
.focusGroup()
.focusProperties {
onEnter = { runCatching { searchFieldFocusRequester.requestFocus() } }
},
)I can see why it had to go — onEnter fires every time focus re-enters the screen, so it was fighting restoreFocusTrigger and yanking focus back to the search field the moment the podcast overlay closed. Good change in intent. Two things I'd check before merge, though:
1. LaunchedEffect(Unit) + requestFocus() is the racy variant, and runCatching hides the failure.
FocusRequester.requestFocus() throws if the target node isn't placed yet, and a LaunchedEffect(Unit) body can run before the first layout pass. This repo already knows that — TvEmptyState.kt:41-44 does:
LaunchedEffect(Unit) {
withFrameNanos {}
runCatching { focusRequester.requestFocus() }
}and TvPodcastGridScaffold.kt:55-58 waits on snapshotFlow { …visibleItemsInfo.isNotEmpty() }.first { it } for the same reason. Here there's no guard, so when it loses the race the runCatching swallows it and the Search tab opens with nothing focused — on a TV that means a dead D-pad until the user finds their way back up to the tab bar. It'll be intermittent and device/timing dependent, which is the worst kind. Adding withFrameNanos {} matches the existing precedent and costs one line.
2. tvFocusInactiveWhen now has no focus target of its own to attach to.
tvFocusInactiveWhen is just focusProperties { canFocus = false } (TvDetailOverlay.kt:89-93), and it's applied to the modifier this Column receives (:114). Previously that focusProperties sat directly on the .focusGroup() focus target, so deactivating that one group blocked the entire subtree — which is what the KDoc promises ("Deactivates focus for this subtree"). With focusGroup() gone, this Column has no focus target in its chain, so the deactivation propagates only to whichever focus targets happen to be nearest below it, and stops at each of them.
Note this screen was the only tvFocusInactiveWhen call site with an explicit focusGroup() — the others (TvHomeScreen.kt:81, TvYourPodcastsScreen.kt:69, TvPlaylistsScreen.kt:96) hand it to a root that owns its own grid/list focus target. I'm reasoning from the modifier chain rather than from a device, so treat this as "please verify" rather than a claim: with the podcast detail overlay open over search results, confirm the D-pad genuinely can't move focus among the results underneath in all three scopes (Top Results carousel, Podcasts grid, Episodes grid — the two grids each add their own focusGroup(), so they're the interesting ones). If it can, restoring .focusGroup() without the onEnter gets the subtree contract back while keeping the fix above.
|
@geekygecko thanks for the feedback, valid points! Screen_recording_20260814_105042.mp4 |
4cdc38e to
eb644a0
Compare
|
Claude finished @sztomek's task in 4m 6s —— View job Code review: TV Search Podcasts/Episodes scope filter
Re-reviewed after
Also confirmed the Retracting one item from my last pass: I flagged that dropping 1. Still the one behavioural gap: episode actions are long-press-only in the Episodes scope (thread)
If the grid cell can't spare the horizontal room for the more button, an overflow glyph in the focused card would do. Either way, 2. Episodes grid padding (inline)
3.
|


Description
Adds the Podcasts/Episodes scope filter to the TV Search screen, on top of the basic search PR.
Top Results/Podcasts/Episodes, styled exactly as the top‑bar tab pills (androidx.tv.material3TabRow+ pill indicator in abackgroundSunkencontainer), centered between two divider lines per the Figma. Selecting follows focus, like the top bar.TvPodcastGridScaffold).filterstate +onFilterSelectedtoTvSearchViewModeland the scope switching inTvSearchScreen. No new strings (reusessearch_filters_*), no analytics.Fixes POC-800 https://linear.app/a8c/issue/POC-800/wire-up-search-apis
Figma: Ftk3KwnfqaK4g57yCN63p0-fi-2595_2243
Testing Instructions
Screenshots or Screencast
Screen_recording_20260812_142126.mp4
Checklist
./gradlew spotlessApplyto automatically apply formatting/linting)modules/services/localization/src/main/res/values/strings.xmlI have tested any UI changes...